#include "sysserversocket.h"
#include "server-socket.h"
#include "commonfunc.h"
#include <QDebug>
#include "sysserverclientsocket.h"

CNS_SYS_REGISTER(SysServerSocket)

CNS_INIT(ServerSocketProc)
//CNS_REGISTER_CNS_COM_FACTORY2(ServerSocketProc, CnsComponent)
CNS_REGISTER_CNS_COM_FACTORY(ServerSocketProc)
CNS_INIT_END

SysServerSocket::SysServerSocket()
{

}

void SysServerSocket::_on_watchdog_timer(uv_timer_t* handle)
{
    ServerSocketProc* server = reinterpret_cast<ServerSocketProc*>(handle->data);

    ServerClientSocketProc* client = TAILQ_FIRST(&server->getClientList());
    while (client != nullptr) {
        if (client->beats == 0) {
            qDebug() << QString("The client(%1) goes down").arg(client->addr);
        }

        client->beats = 0;
        client = TAILQ_NEXT(client, link);
    }
/*
    static unsigned int _COUNTER = 0;
    static char* _ROLLER_LIST = "|/-\\";

    fprintf(stderr, "\rThe watch dog is running... %c", _ROLLER_LIST[_COUNTER & 0x3]);
    _COUNTER++;
*/
}

int SysServerSocket::server_start(ServerSocketProc* server)
{
    struct sockaddr_in addr;

    uv_timer_init(server->getLoop(), &server->getWatchDog());
    server->getWatchDog().data = server;
    uv_timer_start(&server->getWatchDog(), SysServerSocket::_on_watchdog_timer, 5000, 5000);

    uv_tcp_init(server->getLoop(), &server->getSocket());
    server->getSocket().data = server;

    uv_ip4_addr(server->getIp().toLatin1(), server->getPort(), &addr);
    uv_tcp_bind(&server->getSocket(), reinterpret_cast<const struct sockaddr*>(&addr), 0);

    uv_listen(reinterpret_cast<uv_stream_t*>(&server->getSocket()), 32, _on_connect);

    return 0;
}

void SysServerSocket::f_run(CnsComponentPtr com, uv_loop_t *loop)
{
    ServerSocketProc* serverSocketProc = static_cast<ServerSocketProc*>(com.data());
    server_initialize(loop, serverSocketProc);
    serverSocketProc->getAsync()->data = loop;

    uv_async_init(loop, serverSocketProc->getAsync(), uv_async_stop_loop);
    server_start(serverSocketProc);

    qDebug() << QString("socket-server %1:%2 starts...").arg(serverSocketProc->getIp()).arg(serverSocketProc->getPort());

}

void SysServerSocket::f_server_detach(ServerSocketProc* server, ServerClientSocketProc* client)
{
    TAILQ_REMOVE(&server->getClientList(), client, link);
}

void SysServerSocket::server_destroy(ServerSocketProc* server)
{
//    assert(TAILQ_FIRST(&server->getClientList()));
}

void SysServerSocket::_on_connect(uv_stream_t* stream, int status)
{
    static uint32_t index =0;
    if (status == -1) return;

    ServerSocketProcPtr server(reinterpret_cast<ServerSocketProc*>(stream->data));

//    ServerClientSocketProcPtr client(static_cast<ServerClientSocketProc*>(g_framework->newComponent("client"+QString().setNum(index), "/server/.//clientSocket").data()));
    ServerClientSocketProcPtr client = FRAMEWORK->newComponent("client"+QString().setNum(index), "/server/.//serverClientSocket").staticCast<ServerClientSocketProc>();
    if (client.isNull()) return;

    SYS(SysServerClientSocket)->client_initialize(client, server);

    if (uv_accept(stream, reinterpret_cast<uv_stream_t*>(&client->socket)) != 0) {
        close_client(client);
        return;
    }

    struct sockaddr_in peername;

    int namelen = sizeof peername;
    int r = uv_tcp_getpeername(&client->socket, reinterpret_cast<sockaddr*>(&peername), &namelen);
    char addr[17];
    if(r){
        close_client(client);
        return;
    } else{
        uv_ip4_name(&peername, addr, sizeof addr);
    }
    client->addr = addr;
    client->addr.append(QString(":%1:%2").arg(server->getPort()).arg(peername.sin_port));

    SYS(SysServerClientSocket)->client_read(client.data());
    TAILQ_INSERT_TAIL(&server->getClientList(), client.data(), link);
}


void SysServerSocket::close_client(ServerClientSocketProcPtr client)
{
    uv_close(reinterpret_cast<uv_handle_t*>(&client->socket), nullptr);
    client->exec("_on_disconnect");
}

int SysServerSocket::server_initialize(uv_loop_t* loop, ServerSocketProc* server)
{

    server->setLoop(loop);
    TAILQ_INIT(&server->getClientList());

    return 0;
}
